-
Notifications
You must be signed in to change notification settings - Fork 375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added empty result set check to nextRowset() #520
Added empty result set check to nextRowset() #520
Conversation
source/pdo_sqlsrv/pdo_stmt.cpp
Outdated
CHECK_CUSTOM_ERROR( has_fields == 0, driver_stmt, SQLSRV_ERROR_NO_FIELDS ) { | ||
throw core::CoreException(); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From this page it seems valid and accurate to call this without making another trip to the server. Please confirm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since a statement has to be executed for the user to call nextRowset, there is no trip to the server made here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an equivalent scenario in the sqlsrv side?
Based on your comment it seems that fetch() also invokes SQLNumResultCols(), right? Would that be a duplicate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I've verified the same problem exists on the sqlsrv side. But I think core_sqlsrv_fetch is never called if nextRowset is called before fetch, which is why we're catching the error here. I'll add a fix and test for sqlsrv.
Moved the error check to core_sqlsrv_next_result in core_stmt.cpp. Edit: Moved it back because it was causing test failures. |
source/pdo_sqlsrv/pdo_stmt.cpp
Outdated
CHECK_CUSTOM_ERROR( !has_result, driver_stmt, SQLSRV_ERROR_NO_FIELDS ) { | ||
throw core::CoreException(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite understand. I thought PSOStatement::nextRowset should only be called on a fetched statement. The condition for if says fetched is NOT called, and throws an exception if there is no result. How can there any result if the statement was never fetched?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nextRowset() should only be called on a fetched statement, but there is nothing stopping the user from calling nextRowset immediately after executing, and we need to handle that case. In issue 507 it occurs when going into the 'else' part of stored procedure that returns a result set in the 'if' but not the 'else' branch, so it can happen that user does call nextRowset first accidentally via a stored procedure.
This code catches the case where nextRowset() is called before fetch on a null result set, giving the appropriate error message; before, there would be no error given. In the case where nextRowset is called before fetch on non-null result set, core_sqlsrv_has_any_result will return true and the error is not triggered. In the event that nextRowset is called after fetch() and the result set is null, that will be handled in core_sqlsrv_next_result() (or by the check for past_next_result_end above). I think that covers all the relevant cases...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea I was confused what happens when fetch isn't called and has_result is true, but now it's more clear. Can you put a comment saying if fetch_called is false and has_result is true, core_sqlsrv_next_result will handle it.
$conn = new PDO( "sqlsrv:Server = $server; Database = $databaseName; ", $uid, $pwd ); | ||
$conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); | ||
|
||
$stmt = $conn->query("IF OBJECT_ID('TestEmptySetTable', 'U') IS NOT NULL DROP TABLE TestEmptySetTable"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There a DropTable and DropProc function you can use in MsCommon
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps add a couple of tests on prepare (not query / exec) on a regular SELECT query (not a stored procedure) then fetch next rowset, with and without client buffers?
--TEST-- | ||
Test that calling nextRowset() on an empty result set produces the correct error message. Fix for Github 507. | ||
--SKIPIF-- | ||
<?php require('skipif.inc'); ?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please try to limit the length of test title. If you have more please put it in --DESCRIPTION-- instead.
For tests, it's a good practice to drop test tables / procedures at the end to leave no trace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do drop them at the end, but I kept the drop functions at the beginning too just in case...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahhh... I see! In the beginning you use DropTable and DropProc but later you simply use queries.
$stmt = sqlsrv_query($conn, "DROP TABLE TestEmptySetTable"); | ||
$stmt = sqlsrv_query($conn, "DROP PROCEDURE TestEmptySetProc"); | ||
sqlsrv_close($conn); | ||
?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just for practice, probably free the statement object too before closing the connection?
sqlsrv_close($conn); | ||
?> | ||
--EXPECTF-- | ||
Return a nonempty result set: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why EXPECTF?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
%s in the output may be either 'Microsoft' on Windows or 'unixODBC' on unix systems, and EXPECTREGEX seemed like overkill for one string in a long output like this.
I'm not sure how useful testing with a regular SELECT statement would be, because I don't think the result set could ever be null in that case. At most you can have an empty result set with no rows but at least one column, or you select from a nonexistent column, which yields a different error message. These are not the same as a null result set, in which there are no rows and no columns, and it's why I needed to use the stored procedure in the tests to trigger a null result. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approve assuming you have checked there's no regression
--TEST-- | ||
Test that calling nextRowset() on an empty result set produces the correct error message. Fix for Github 507. | ||
--SKIPIF-- | ||
<?php require('skipif.inc'); ?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahhh... I see! In the beginning you use DropTable and DropProc but later you simply use queries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My oversight. Looks like you still have test failures. Please fix those.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the test that you had to fix the regex in order to pass, consider only checking for the first error (the PHP related error)
This is a fix for the bug discussed in issue #507.